import { TableDatas } from './table-datas';
import { Config } from './config';
import { Inject } from '../decorators/inject';
import { NExcel } from '../types';
import { Injectable } from '../decorators/inject';
import { DrawHelper } from '../utils/draw-helper';
import { TableEvent } from './table-event';
import { Hook } from './hook';
import { MergeCell } from './merge-cell';
import { TMergeCell } from '../types/common.interface';
import { Cache } from './cache';
import { catchErr } from '../utils/decorates';
import { FreezeRender } from './freeze-render';
@Injectable('TableBackGround')
export class TableBackGround {
    protected ctx: CanvasRenderingContext2D;
    protected drawLine: (p: number, type: 'row' | 'col') => void = () => { };
    public viewBoundary: {
        start: {
            row: number,
            col: number
        },
        end: {
            row: number,
            col: number
        }
    };
    constructor(
        protected datas: TableDatas,
        protected conf: Config,
        @Inject(NExcel.CanvasToken) protected canvasEle: HTMLCanvasElement,
        protected tableEvent: TableEvent,
        protected hook: Hook,
        protected mergeCell: MergeCell,
        protected cache: Cache,
        protected freezeRender: FreezeRender
    ) {

    }
    protected init() {
        this.ctx = this.canvasEle.getContext('2d');
        this.drawLine = (() => {
            const drawLine = DrawHelper.line(this.ctx)({ width: 1, type: 'solid', color: 'rgb(170, 170, 170)' });
            return (p: number, type: 'row' | 'col') => {
                if (type === 'row') {
                    drawLine({ x: this.conf.header.row.width, y: p }, { x: this.canvasEle.width, y: p });
                } else {
                    drawLine({ x: p, y: this.conf.header.col.height }, { x: p, y: this.canvasEle.height });
                }
            }
        })()
    }
    protected renderCommon() {
        for (let row = this.cache.rowBoundary.start; row < this.cache.rowBoundary.end + 1; row++) {
            let [_, end] = this.cache.getRowPositon(row);
            this.drawLine(end, 'row');
        }
        for (let col = this.cache.colBoundary.start; col < this.cache.colBoundary.end + 1; col++) {
            let [_, end] = this.cache.getColPosition(col);
            this.drawLine(end, 'col');
        }
        this.conf.visibleBoundary.row = this.cache.rowBoundary.start;
        this.conf.visibleBoundary.endRow = this.cache.rowBoundary.end;
        this.conf.visibleBoundary.col = this.cache.colBoundary.start;
        this.conf.visibleBoundary.endCol = this.cache.colBoundary.end;
        this.viewBoundary = {
            start: {
                row: this.cache.rowBoundary.start,
                col: this.cache.colBoundary.start
            },
            end: {
                row: this.cache.rowBoundary.end,
                col: this.cache.colBoundary.end
            }
        };
        this.tableEvent.render$.next({ type: 'afterBackgroundRender' });
    }
    protected renderFreeze() {
        const freezeRow = this.conf.freeze.row;
        const freezeCol = this.conf.freeze.col;
        if (freezeRow !== -1) {
            let lastHeight = this.conf.header.col.height;
            for (let row = 0; row < freezeRow; row++) {
                let height = this.datas.getRowHeight(row);
                lastHeight = height + lastHeight
                this.drawLine(lastHeight, 'row');
            }
        }
        if (freezeCol !== -1) {
            let lastWidth = this.conf.header.row.width;
            for (let col = 0; col < freezeCol; col++) {
                let width = this.datas.getColWidth(col);
                lastWidth = lastWidth + width;
                this.drawLine(lastWidth, 'col');
            }
        }
    }
    protected embellishMergeCell(mergeCell: TMergeCell): TMergeCell {
        let temp = {
            row: this.cache.rowBoundary.start,
            col: this.cache.colBoundary.start,
            rowspan: this.cache.rowBoundary.end - this.cache.rowBoundary.start + 1,
            colspan: this.cache.colBoundary.end - this.cache.colBoundary.start + 1
        }
        if (
            (this.mergeCell.containMergeCell(mergeCell, temp) == false && this.mergeCell.containMergeCell(temp, mergeCell) == false)
            && (this.mergeCell.hasUnion(mergeCell, temp) == false && this.mergeCell.hasUnion(temp, mergeCell) == false)
        ) {
            return null;
        }
        const start = this.viewBoundary.start;
        const end = this.viewBoundary.end;
        let newStart = { row: mergeCell.row, col: mergeCell.col };
        let newEnd = { row: mergeCell.row + mergeCell.rowspan - 1, col: mergeCell.col + mergeCell.colspan - 1 };
        if (newStart.row < start.row) {
            newStart.row = start.row;
        }
        if (newStart.col < start.col) {
            newStart.col = start.col;
        }
        if (newEnd.row > end.row) {
            newEnd.row = end.row;
        }
        if (newEnd.col > end.col) {
            newEnd.col = end.col;
        }
        return {
            row: newStart.row,
            col: newStart.col,
            rowspan: newEnd.row - newStart.row + 1,
            colspan: newEnd.col - newStart.col + 1
        };

    }
    protected renderMergeFullRow(mergeCell: TMergeCell) {
        let range = { col: mergeCell.col, colspan: mergeCell.colspan };
        if (range.col < this.viewBoundary.start.col) {
            range.col = this.viewBoundary.start.col;
            range.colspan = mergeCell.colspan - (this.viewBoundary.start.col - mergeCell.col);
        }
        if (range.col + range.colspan - 1 > this.viewBoundary.end.col) {
            range.colspan = this.viewBoundary.end.col - range.col - 1;
        }
        let xRange = this.cache.getColRangePositionInfo(range.col, range.col + range.colspan - 1);
        this.ctx.save();
        this.ctx.clearRect(xRange.start + 1, this.conf.header.col.height + 1, xRange.end - xRange.start - 2, this.canvasEle.height - this.conf.header.col.height - 2);
        this.ctx.restore();
    }
    protected renderMergeFullCol(mergeCell: TMergeCell) {
        let range = { row: mergeCell.row, rowspan: mergeCell.rowspan };
        if (range.row < this.viewBoundary.start.row) {
            range.row = this.viewBoundary.start.row;
            range.rowspan = mergeCell.rowspan - (this.viewBoundary.start.row - mergeCell.row);
        }
        if (range.row + range.rowspan - 1 > this.viewBoundary.end.row) {
            range.rowspan = this.viewBoundary.end.row - range.row - 1;
        }
        let yRange = this.cache.getRowRangePositionInfo(range.row, range.row + range.rowspan - 1);
        this.ctx.save();
        this.ctx.clearRect(this.conf.header.row.width + 1, yRange.start + 1, this.canvasEle.width - this.conf.header.row.width - 2, yRange.end - yRange.start - 2);
        this.ctx.restore();
    }
    @catchErr()
    protected renderNormalMergeCell(mergeCell: TMergeCell) {
        let ranges = this.mergeCell.spliteMerge(mergeCell).map(item => {
            return {
                ...this.mergeCell.getAfterSplitMergeRectMerge(item),
                origin: mergeCell
            }
        });
        for (let range of ranges) {
            const rectInfo = this.cache.getRangeRectInfo({ row: range.row, col: range.col }, { row: range.row + range.rowspan - 1, col: range.col + range.colspan - 1 });
            this.ctx.save();
            if ((this.conf.freeze.row !== -1 || this.conf.freeze.col !== -1) && (mergeCell.row > this.conf.freeze.row || mergeCell.col > this.conf.freeze.col)) {
                let y = rectInfo.start.y + 1;
                let x = rectInfo.start.x + 1;
                let h = rectInfo.h - 2;
                let w = rectInfo.w - 2;
                let startY = this.conf.header.col.height + this.freezeRender.getStartRowHeight();
                if (this.conf.freeze.row !== -1 && mergeCell.row > this.conf.freeze.row && y < startY) {
                    h = h - (y - startY)
                    y = startY + 1;
                }
                this.ctx.clearRect(x, y, w, h);
            } else {
                this.ctx.clearRect(rectInfo.start.x + 1, rectInfo.start.y + 1, rectInfo.w - 2, rectInfo.h - 2);
            }
            this.ctx.restore();
        }
    }
    protected renderMergeCells() {
        for (let mergeCell of this.mergeCell.mergeCells) {
            if (mergeCell.row === -1 || mergeCell.col === -1) {
                if (mergeCell.row === -1 && mergeCell.col !== -1) {
                    this.renderMergeFullRow(mergeCell);
                }
                if (mergeCell.row !== -1 && mergeCell.col === -1) {
                    this.renderMergeFullCol(mergeCell);
                }
            } else {
                // console.log(mergeCell)
                this.renderNormalMergeCell({ ...mergeCell });
            }
        }
    }
    public render() {
        this.ctx.save();
        const canvasW = Number(this.canvasEle.style.width.replace('px', ''));
        const canvasH = Number(this.canvasEle.style.height.replace('px', ''));
        const startX = (this.conf.header.row.width + 1) // this.conf.pixelRatio;
        const startY = (this.conf.header.col.height + 1) // this.conf.pixelRatio;
        const width = canvasW * this.conf.pixelRatio - this.conf.header.row.width;
        const height = canvasH * this.conf.pixelRatio - this.conf.header.col.height;
        this.ctx.clearRect(startX, startY, width + 1, height + 1)
        this.renderCommon();
        this.renderFreeze();
        this.renderMergeCells();
        this.hook.afterBgRender.excute();
        this.ctx.restore();
    }
}

